/*
 * eID Test PKI Project.
 * Copyright (C) 2012 FedICT.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License version
 * 3.0 as published by the Free Software Foundation.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, see 
 * http://www.gnu.org/licenses/.
 */

package be.fedict.eid.pki.entity;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.Serializable;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Date;
import java.util.List;

import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.Lob;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Query;
import javax.persistence.Transient;

import org.joda.time.DateTime;

@Entity
@NamedQueries({ @NamedQuery(name = CertificateAuthorityEntity.ALL, query = "FROM CertificateAuthorityEntity") })
public class CertificateAuthorityEntity implements Serializable {

	private static final long serialVersionUID = 1L;

	public static final String ALL = "CertificateAuthorityEntity.all";

	private String name;

	private String subject;

	private int keySize;

	private Date notBefore;

	private Date notAfter;

	private byte[] encodedCertificate;

	private byte[] encodedPrivateKey;

	private int crlValidity;

	private int crlOverlap;

	private int crlCount;

	private String interSubject;

	private String interPolicyOid;

	private int interValidity;

	private int interSpawnInterval;

	private int interSpawnCount;

	private int interCount;

	public CertificateAuthorityEntity() {
		super();
	}

	public CertificateAuthorityEntity(String name, String subject, int keySize,
			DateTime notBefore, DateTime notAfter, X509Certificate certificate,
			int crlValidity, int crlOverlap, PrivateKey privateKey,
			String interSubject, String interPolicyOid, int interValidity,
			int interSpawnInterval, int interSpawnCount) {
		this.name = name;
		this.subject = subject;
		this.keySize = keySize;
		this.notBefore = notBefore.toDate();
		this.notAfter = notAfter.toDate();
		try {
			this.encodedCertificate = certificate.getEncoded();
		} catch (CertificateEncodingException e) {
			throw new RuntimeException(e);
		}
		this.crlValidity = crlValidity;
		this.crlOverlap = crlOverlap;
		this.crlCount = 1;
		this.encodedPrivateKey = privateKey.getEncoded();
		this.interSubject = interSubject;
		this.interPolicyOid = interPolicyOid;
		this.interValidity = interValidity;
		this.interSpawnInterval = interSpawnInterval;
		this.interSpawnCount = interSpawnCount;
	}

	@Id
	public String getName() {
		return this.name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getSubject() {
		return this.subject;
	}

	public void setSubject(String subject) {
		this.subject = subject;
	}

	public int getKeySize() {
		return this.keySize;
	}

	public void setKeySize(int keySize) {
		this.keySize = keySize;
	}

	public Date getNotBefore() {
		return this.notBefore;
	}

	public void setNotBefore(Date notBefore) {
		this.notBefore = notBefore;
	}

	public Date getNotAfter() {
		return this.notAfter;
	}

	public void setNotAfter(Date notAfter) {
		this.notAfter = notAfter;
	}

	@Lob
	@Column(length = 4 * 1024)
	@Basic(fetch = FetchType.LAZY)
	public byte[] getEncodedCertificate() {
		return this.encodedCertificate;
	}

	public void setEncodedCertificate(byte[] encodedCertificate) {
		this.encodedCertificate = encodedCertificate;
	}

	@Lob
	@Column(length = 4 * 1024)
	@Basic(fetch = FetchType.LAZY)
	public byte[] getEncodedPrivateKey() {
		return this.encodedPrivateKey;
	}

	public void setEncodedPrivateKey(byte[] encodedPrivateKey) {
		this.encodedPrivateKey = encodedPrivateKey;
	}

	public int getCrlValidity() {
		return this.crlValidity;
	}

	public void setCrlValidity(int crlValidity) {
		this.crlValidity = crlValidity;
	}

	public int getCrlOverlap() {
		return this.crlOverlap;
	}

	public void setCrlOverlap(int crlOverlap) {
		this.crlOverlap = crlOverlap;
	}

	public int getCrlCount() {
		return this.crlCount;
	}

	public void setCrlCount(int crlCount) {
		this.crlCount = crlCount;
	}

	public String getInterSubject() {
		return this.interSubject;
	}

	public void setInterSubject(String interSubject) {
		this.interSubject = interSubject;
	}

	public String getInterPolicyOid() {
		return this.interPolicyOid;
	}

	public void setInterPolicyOid(String interPolicyOid) {
		this.interPolicyOid = interPolicyOid;
	}

	public int getInterValidity() {
		return this.interValidity;
	}

	public void setInterValidity(int interValidity) {
		this.interValidity = interValidity;
	}

	public int getInterSpawnInterval() {
		return this.interSpawnInterval;
	}

	public void setInterSpawnInterval(int interSpawnInterval) {
		this.interSpawnInterval = interSpawnInterval;
	}

	public int getInterSpawnCount() {
		return this.interSpawnCount;
	}

	public void setInterSpawnCount(int interSpawnCount) {
		this.interSpawnCount = interSpawnCount;
	}

	public int getInterCount() {
		return this.interCount;
	}

	public void setInterCount(int interCount) {
		this.interCount = interCount;
	}

	@Transient
	public int getNextCrlNumber() {
		this.crlCount++;
		return this.crlCount;
	}

	@Transient
	public PrivateKey getPrivateKey() {
		KeyFactory keyFactory;
		try {
			keyFactory = KeyFactory.getInstance("RSA");
		} catch (NoSuchAlgorithmException e) {
			throw new RuntimeException(e);
		}
		KeySpec keySpec = new PKCS8EncodedKeySpec(this.encodedPrivateKey);
		PrivateKey privateKey;
		try {
			privateKey = keyFactory.generatePrivate(keySpec);
		} catch (InvalidKeySpecException e) {
			throw new RuntimeException(e);
		}
		return privateKey;
	}

	@Transient
	public X509Certificate getCertificate() {
		try {
			CertificateFactory certificateFactory = CertificateFactory
					.getInstance("X.509");
			InputStream certificateStream = new ByteArrayInputStream(
					this.encodedCertificate);
			X509Certificate certificate = (X509Certificate) certificateFactory
					.generateCertificate(certificateStream);
			return certificate;
		} catch (CertificateException e) {
			throw new RuntimeException("cert factory error: " + e.getMessage());
		}
	}

	public static List<CertificateAuthorityEntity> getCertificateAuthorities(
			EntityManager entityManager) {
		Query query = entityManager.createNamedQuery(ALL);
		return query.getResultList();
	}
}
